package org.simple.framework.core.util;

import com.sun.codemodel.internal.util.UnicodeEscapeWriter;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.simple.framework.core.annotation.Component;
import org.simple.framework.core.annotation.Controller;
import org.simple.framework.core.annotation.Repository;
import org.simple.framework.core.annotation.Service;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ：zhanghaixuan
 * @date ：Created in 2020/3/7 8:06 下午
 * 用途         ： 类spring 容器
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    /**
     * 存放所有被配置标记的目标对象的MAP
     */
    private final Map<Class<?>,Object> beanMap = new ConcurrentHashMap<Class<?>,Object>();

    /**
     * 加载bean的注解列表
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION = Arrays.asList(Component.class, Service.class, Repository.class, Controller.class);

    public static BeanContainer getInstance(){
        return ContainerHolder.HOLDER.instance;
    }

    /**
     * 使用枚举创建的单例
     */
    private enum ContainerHolder{
        /**
         * 无意义单例
         */
        HOLDER;

        private BeanContainer instance;

        ContainerHolder(){
            instance = new BeanContainer();
        }
    }

    /**
     * 获取bean实例的数量
     */
    public int size(){
        return beanMap.size();
    }

    /**
     * 容器是否被加载过
     */
    private boolean loaded = false;

    /**
     * 是否已经加载过Bean
     */
    public boolean loaded(){
        return loaded;
    }

    /**
     * 扫描加载所有的bean
     */
    public synchronized void loadBeans(String beanPackageName) throws IOException {
        if(loaded){
            log.warn("容器已经被加载过了");
            return;
        }

        Set<Class<?>> classes = ClassUtil.extractPackageClass(beanPackageName);

        if(ValidationUtil.collectionIsEmpty(classes)){
           log.warn("{}提取不到任何bean",beanPackageName);
           return;
        }

        for (Class<?> aClass : classes) {
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
                /**
                 * 类上面是否有这个注解
                 */
                if (aClass.isAnnotationPresent(annotation)) {
                    beanMap.put(aClass,ClassUtil.newInstance(aClass,true));

                }
            }
        }

        loaded = true;
    }

    /**
     * 添加一个class对象和他的实例
     * @param clazz  bean的类型
     * @param bean bean的实例
     * @return Object
     */
    public Object addBean(Class<?> clazz,Object bean){
        return beanMap.put(clazz,bean);
    }

    /**
     * 从容器中删除bean
     * @param clazz bean的类型
     * @return object
     */
    public Object removeBean(Class<?> clazz){
       return beanMap.remove(clazz);
    }

    /**
     * 从容器中获得某个bean
     * @param clazz bean的类型
     * @return object
     */
    public Object getBean(Class<?> clazz){
        return beanMap.get(clazz);
    }

    /**
     * 获取所有注册bean的键值对
     * @return Set<Class<?>>
     */
    public Set<Class<?>> getClasses(){
        return beanMap.keySet();
    }


    /**
     * 获取所有bean的集合
     */
    public HashSet<Object> getBeans(){
        return new HashSet(beanMap.values());
    }

    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
        Set<Class<?>> keySet = getClasses();

        if(ValidationUtil.collectionIsEmpty(keySet)){
            log.warn("容器内部没有任何class对象");
            return null;
        }

        Set<Class<?>> classSet = new HashSet<>();

        for (Class<?> aClass : keySet) {
            if (aClass.isAnnotationPresent(annotation)) {
                classSet.add(aClass);
            }
        }

        return classSet.size()>0?classSet:null;
    }


    public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
        Set<Class<?>> keySet = getClasses();

        if(ValidationUtil.collectionIsEmpty(keySet)){
            log.warn("容器内部没有任何class对象");
            return null;
        }

        Set<Class<?>> classSet = new HashSet<>();

        for (Class<?> aClass : keySet) {
            if (interfaceOrClass.isAssignableFrom(aClass) && !aClass.equals(interfaceOrClass)) {
                classSet.add(aClass);
            }
        }

        return classSet.size()>0?classSet:null;
    }
}
